package org.summerframework.component.security.permissions.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.summerframework.component.mybatisplus.AbstractBasePlusService;
import org.summerframework.component.security.events.PermissionsRelChangeEvent;
import org.summerframework.component.security.permissions.model.dao.UserPermissionsRoleRelDAO;
import org.summerframework.component.security.permissions.model.domain.UserPermissionsRoleRelDO;
import org.summerframework.component.security.permissions.service.UserPermissionsRoleRelService;
import org.summerframework.component.security.permissions.service.UserPermissionsService;
import org.summerframework.component.security.permissions.service.dto.UserPermissionsDTO;
import org.summerframework.component.security.permissions.service.dto.UserPermissionsQueryDTO;
import org.summerframework.component.security.permissions.service.dto.UserPermissionsRoleRelDTO;
import org.summerframework.component.security.permissions.service.dto.UserPermissionsRoleRelQueryDTO;
import org.summerframework.component.security.role.service.UserRoleRelService;
import org.summerframework.component.security.role.service.UserRoleService;
import org.summerframework.component.security.role.service.dto.UserRoleDTO;
import org.summerframework.component.security.role.service.dto.UserRoleRelDTO;
import org.summerframework.core.base.result.DefaultResultCode;
import org.summerframework.core.exception.WebException;
import org.summerframework.core.page.PaginationQuery;
import org.summerframework.core.util.OptionalUtils;
import org.summerframework.core.util.SpringContextUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service("userPermissionsRoleRelService")
public class UserPermissionsRoleRelServiceImpl extends AbstractBasePlusService<UserPermissionsRoleRelDAO, UserPermissionsRoleRelDTO, UserPermissionsRoleRelQueryDTO, UserPermissionsRoleRelDO> implements UserPermissionsRoleRelService {
    @Resource
    private UserPermissionsRoleRelDAO userPermissionsRoleRelDAO;

    @Override
    public Wrapper<UserPermissionsRoleRelDO> getWrapper(UserPermissionsRoleRelQueryDTO query) {

        return Wrappers.<UserPermissionsRoleRelDO>lambdaQuery()
                .orderBy(Objects.nonNull(query.getOrderById()), query.getOrderById() != null ? query.getOrderById() : false, UserPermissionsRoleRelDO::getId)
                .eq(Objects.nonNull(query.getRoleId()), UserPermissionsRoleRelDO::getRoleId, query.getRoleId())
                .eq(Objects.nonNull(query.getPermissionsId()), UserPermissionsRoleRelDO::getPermissionsId, query.getPermissionsId())
                .eq(Objects.nonNull(query.getParentId()), UserPermissionsRoleRelDO::getParentId, query.getParentId())
                .eq(Objects.nonNull(query.getStatus()), UserPermissionsRoleRelDO::getStatus, query.getStatus())
                .in(Objects.nonNull(query.getRoleIds()), UserPermissionsRoleRelDO::getRoleId, PaginationQuery.repairIn(query.getRoleIds()));
    }

    @Resource
    private UserPermissionsService userPermissionsService;

    @Resource
    private UserRoleRelService userRoleRelService;

    @Resource
    private UserRoleService userRoleService;

    /**
     * 修复未传子节点数据时
     *
     * @param permissionsIds 权限id
     */
    private void repairDataChild(List<Long> permissionsIds) {
        List<Long> temp = new ArrayList<>();
        permissionsIds.forEach(id -> {
            UserPermissionsQueryDTO query = new UserPermissionsQueryDTO();
            query.setParentId(id);
            query.setStatus(1);
            query.setIsPagination(false);
            List<UserPermissionsDTO> userPermissionsDTOS = userPermissionsService.selectByQuery(query);
            List<Long> collect = userPermissionsDTOS.stream().map(UserPermissionsDTO::getId).collect(Collectors.toList());

            if (permissionsIds.stream().anyMatch(collect::contains)) {
                if (!permissionsIds.containsAll(collect)) {
                    throw new WebException(DefaultResultCode.ILLEGAL_ARGUMENT);
                }
            } else if (CollectionUtils.isNotEmpty(collect)) {
                repairDataChild(collect);//先修复数据源, 在添加到最终的节点上. 不然会出现孙子节点丢失
                temp.addAll(collect);
            }
        });

        permissionsIds.addAll(temp);
    }

    @Override
    public Integer insertRepairParentId(Long parentId, Long objectId) {
        return userPermissionsRoleRelDAO.insertRepairParentId(parentId, objectId);
    }

    @Override
    public Integer updatePermissionsIdsByUserId(List<Long> permissionsIds, Long roleId) {
        if (roleId == 1L) {
            return 0;
        }
        if (permissionsIds == null) {
            return 0;
        }
        repairDataChild(permissionsIds);//修复数据源
        permissionsIds.remove(0L);//不能添加顶级节点

        List<UserPermissionsRoleRelDTO> userPermissionsRoleRels = selectByRoleId(roleId);

        List<Long> deletePermissionsIds = new ArrayList<>();

        //根据已选择的所有数据判断需要删除/添加的节点
        userPermissionsRoleRels.forEach(permissionsRoleRel -> {
            if (!permissionsIds.contains(permissionsRoleRel.getPermissionsId())) {
                deletePermissionsIds.add(permissionsRoleRel.getId());
            } else {
                permissionsIds.remove(permissionsRoleRel.getPermissionsId());
            }
        });

        deletePermissionsIds.forEach(this::deleteById);

        saveBatch(permissionsIds.stream().map(permissionId -> {
            UserPermissionsRoleRelDO userPermissionsRoleRelDO = fromDTO2DO(cover(OptionalUtils.get(userPermissionsService.selectById(permissionId))));

            UserRoleDTO userRoleDTO = OptionalUtils.get(userRoleService.selectById(roleId));
            userPermissionsRoleRelDO.setRoleId(roleId);
            userPermissionsRoleRelDO.setRoleCode(userRoleDTO.getCode());
            userPermissionsRoleRelDO.setRoleName(userRoleDTO.getName());
            userPermissionsRoleRelDO.setRoleType(userRoleDTO.getType());
            userPermissionsRoleRelDO.setRoleRelId(userRoleDTO.getRelId());

            return userPermissionsRoleRelDO;
        }).collect(Collectors.toSet()));


        //推送事件
        if (!deletePermissionsIds.isEmpty()) {
            SpringContextUtil.publishEvent(new PermissionsRelChangeEvent(BASE_OP.UPDATE, roleId, deletePermissionsIds));
        }

        if (!permissionsIds.isEmpty()) {
            SpringContextUtil.publishEvent(new PermissionsRelChangeEvent(BASE_OP.INSERT, roleId, permissionsIds));
        }

        return permissionsIds.size() + deletePermissionsIds.size();
    }

    @Override
    public List<UserPermissionsRoleRelDTO> selectByRoleId(Long roleId) {
        UserPermissionsRoleRelQueryDTO query = new UserPermissionsRoleRelQueryDTO();
        query.setIsPagination(false);
        query.setRoleId(roleId);
        return selectByQuery(query);
    }

    @Override
    public Integer updateById(UserPermissionsDTO userPermissionsDTO) {

        UserPermissionsRoleRelQueryDTO userPermissionsRoleRelQueryDTO = new UserPermissionsRoleRelQueryDTO();
        userPermissionsRoleRelQueryDTO.setPermissionsId(userPermissionsDTO.getId());
        userPermissionsRoleRelQueryDTO.setIsPagination(false);


        List<UserPermissionsRoleRelDTO> userPermissionsRoleRelDTOS = selectByQuery(userPermissionsRoleRelQueryDTO);

        final UserPermissionsRoleRelDTO cover = cover(userPermissionsDTO);

        userPermissionsRoleRelDTOS.forEach(userPermissionsRoleRelDTO -> {
            cover.setId(userPermissionsRoleRelDTO.getId());
            updateById(cover);
        });

        return userPermissionsRoleRelDTOS.size();
    }

    @Override
    public Integer updateById(UserPermissionsRoleRelDTO baseDTO) {
        final UserPermissionsRoleRelDTO oldRel = selectById(baseDTO.getId());
        Integer count = super.updateById(baseDTO);

        if (baseDTO.getParentId() != null) {
            //当原父类下没有权限时删除
            UserPermissionsRoleRelQueryDTO userPermissionsRoleRelQueryDTO = new UserPermissionsRoleRelQueryDTO();
            userPermissionsRoleRelQueryDTO.setParentId(baseDTO.getParentId());

            if (selectCountByQuery(userPermissionsRoleRelQueryDTO) == 0) {
                deleteByRoleIdObjectId(oldRel.getRoleId(), oldRel.getParentId());
            }
        }

        return count;
    }

    @Override
    public Integer deleteByObjectId(Long objectId) {

        return userPermissionsRoleRelDAO.delete(getWrapper(UserPermissionsRoleRelQueryDTO.builder().permissionsId(objectId).build()));
    }

    public Integer deleteByRoleIdObjectId(Long roleId, Long objectId) {
        return userPermissionsRoleRelDAO.delete(getWrapper(UserPermissionsRoleRelQueryDTO.builder().roleId(roleId).permissionsId(objectId).build()));
    }

    @Override
    public void repairData(Long parentId, Map<Long, List<UserPermissionsRoleRelDTO>> groupParentId) {
        if (parentId.equals(0L)) {
            return;
        }
        try {
            UserPermissionsDTO userPermissionsDTO = OptionalUtils.get(userPermissionsService.selectById(parentId));

            List<UserPermissionsRoleRelDTO> userPermissionsRoleRelDTOS = groupParentId.get(userPermissionsDTO.getParentId());

            //当没有父级或者父级不包含自己时
            if (userPermissionsRoleRelDTOS == null || userPermissionsRoleRelDTOS.stream().noneMatch(status -> Objects.equals(status.getPermissionsId(), parentId))) {
                //添加父节点数据
                UserPermissionsRoleRelDTO child = groupParentId.get(parentId).get(0);
                UserPermissionsRoleRelDTO userPermissionsRoleRelDTO = cover(userPermissionsDTO);
                userPermissionsRoleRelDTO.setRoleId(child.getRoleId());
                userPermissionsRoleRelDTO.setRoleCode(child.getRoleCode());
                userPermissionsRoleRelDTO.setRoleName(child.getRoleName());
                userPermissionsRoleRelDTO.setRoleType(child.getType());
                userPermissionsRoleRelDTO.setRoleRelId(child.getRoleRelId());
                userPermissionsRoleRelDTO.setPermissionsId(userPermissionsDTO.getId());
                userPermissionsRoleRelDTO.setPermissionsName(userPermissionsDTO.getName());
                userPermissionsRoleRelDTO.setPermissionsCode(userPermissionsDTO.getCode());
                List<UserPermissionsRoleRelDTO> list = groupParentId.computeIfAbsent(userPermissionsDTO.getParentId(), key -> new ArrayList<>());
                list.add(userPermissionsRoleRelDTO);
                repairData(userPermissionsDTO.getParentId(), groupParentId);
            }
        } catch (Exception e) {
            throw new WebException(DefaultResultCode.ILLEGAL_ARGUMENT, e);
        }
    }

    @Override
    public Integer deleteByRoleId(Long id) {
        return userPermissionsRoleRelDAO.delete(getWrapper(UserPermissionsRoleRelQueryDTO.builder().roleId(id).build()));
    }

    @Override
    public Integer updateByRoleId(UserPermissionsRoleRelDTO dto) {
        return userPermissionsRoleRelDAO.update(fromDTO2DO(dto), Wrappers.<UserPermissionsRoleRelDO>lambdaUpdate().eq(UserPermissionsRoleRelDO::getRoleId, dto.getRoleId()));
    }

    @Override
    public boolean check(BASE_OP baseOp, UserPermissionsRoleRelDTO baseDTO) {
        if (baseOp != BASE_OP.UPDATE) {
            super.check(baseOp, baseDTO);
        } else {
            if (baseDTO.getId() == null && baseDTO.getParentId() == null && baseDTO.getRoleId() == null) {
                throw new WebException(DefaultResultCode.ILLEGAL_ARGUMENT);
            }
        }
        return true;
    }

    @Override
    protected UserPermissionsRoleRelQueryDTO doConstructQuery(UserPermissionsRoleRelQueryDTO query) {
        if (query.getUserId() != null && CollectionUtils.isEmpty(query.getRoleIds())) {
            query.setRoleIds(userRoleRelService.selectByUserId(query.getUserId()).stream().map(UserRoleRelDTO::getRoleId).collect(Collectors.toList()));
        }

        return query;
    }
}
